package com.multiplay;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import android.util.Log;

public class ServerMainLoop extends BaseNetLoop implements Runnable 
{
    private ServerSocketChannel srv_channel = null;
    private SocketChannel cli_channel = null;
    private DatagramChannel broadcast_channel = null;
    private InetSocketAddress la = null;

    public ServerMainLoop(Object p) throws IOException
    {
        super(p);

        srv_channel = ServerSocketChannel.open();
        srv_channel.configureBlocking(false);

        broadcast_channel = DatagramChannel.open();
        broadcast_channel.configureBlocking(false);

        ServerSocket sock = srv_channel.socket();
        la = new InetSocketAddress(GlobalDefs.SERVER_TCP_PORT);
        sock.bind(la);

        DatagramSocket broadcast_socket = broadcast_channel.socket();
        la = new InetSocketAddress(GlobalDefs.BROADCAST_PORT);
        broadcast_socket.bind(la);
        broadcast_socket.setBroadcast(true);

        srv_channel.register(selector, SelectionKey.OP_ACCEPT, srv_channel);
        broadcast_channel.register(selector, SelectionKey.OP_READ, broadcast_channel);
    }

    private void cleanup()
    {
        try 
        {
            selector.close();
            if (srv_channel != null)
                srv_channel.close();
            if (broadcast_channel != null)
                broadcast_channel.close();
        }
        catch (IOException e) 
        {
            Log.e(GlobalDefs.LOG_APP_TAG, e.getMessage());
        }
    }

    public void run() 
    {
        int channels;

        Log.v(GlobalDefs.LOG_APP_TAG, "Thread started");
        while (!is_stopping())
        {
            try
            {
                channels = selector.select();
                if (channels == 0)
                    continue;

                SelectionKey key = null;
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();

                while (it.hasNext())
                {
                    Log.v(GlobalDefs.LOG_APP_TAG, "Got selected key");

                    key = it.next();
                    it.remove();

                    if (key.isAcceptable())
                    {
                        Log.v(GlobalDefs.LOG_APP_TAG, "New client accepted");

                        cli_channel = srv_channel.accept();
                        if (cli_channel == null)
                            continue;

                        Log.v(GlobalDefs.LOG_APP_TAG, "Got cleint socket");
                        cli_channel.configureBlocking(false);
                        cli_channel.register(selector, SelectionKey.OP_READ, cli_channel);
                    }

                    if (key.isReadable())
                    {
                        Log.v(GlobalDefs.LOG_APP_TAG, "Got read event from client socket");
                        
                        if (key.attachment() instanceof SocketChannel)
                        {
                            Log.v(GlobalDefs.LOG_APP_TAG, "Got read event from client TCP socket");
                            cli_channel.close();
                            cli_channel = null;
                        }
                        else if (key.attachment() instanceof DatagramChannel)
                        {
                            Log.v(GlobalDefs.LOG_APP_TAG, "Got read event from client broadcast");
                            ByteBuffer data = ByteBuffer.allocate(8);
                            //broadcast_channel.connect(la);
                            broadcast_channel.receive(data);
                            int client_ip = 0;
                            Log.v(GlobalDefs.LOG_APP_TAG, "Read bytes: " + data.position());
                            if (data.position() == 8)
                            {
                                data.position(0);
                                IntBuffer t = data.asIntBuffer();
                                int sign = t.get();
                                client_ip = t.get();
                                Log.v(GlobalDefs.LOG_APP_TAG, "Sign: " + sign + " ip: " + client_ip);
                            }

                            if (client_ip != 0)
                            {
                                int sign = GlobalDefs.DISCOVERER_SIGNATURE;
                                data.clear();
                                IntBuffer t = data.asIntBuffer();
                                t.put(sign);
                                t.put(ip_address);
                                
                                byte [] quads = BaseNetLoop.ip_addr_int2bytes(client_ip);
                                InetAddress ia = InetAddress.getByAddress(quads);
                                InetSocketAddress addr = new InetSocketAddress(ia, GlobalDefs.ANSWER_UDP_PORT);
                                
                                int len = broadcast_channel.send(data, addr);
                                Log.v(GlobalDefs.LOG_APP_TAG, "Broadcast answer sent. len = " + len);
                            }
                            //broadcast_channel.close();
                            //broadcast_channel = null;
                        }

                        key.cancel();
                    }
                }
            } 
            catch (IOException e) 
            {
                System.err.println(e.getMessage());
                stop = true;
            }
        }
        Log.v(GlobalDefs.LOG_APP_TAG, "Thread finished");
        cleanup();
    }
}
